/*
Reversi.
Copyright (C) <2008>  <Vladimir Simo, Kristian Valentin, Michal Vrabel, Tibor Balazs>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include "main.h"
//#include "stdio.h"

class Node {
private:
	Node *son;
	Node *brother;
	int eval;
	int color;
	Coordinates *coor;
public:
	typPole pole;
public:
	Node();
	void setColor(int newColor) {color = newColor;};
	int getColor() {return color;};
	void setSon(Node *n) {son = n;};
	Node *getSon() {return son;};
	void addBrother(Node *n);

	Node *getBrother();
	Node *getLastBrother();

	void setEvaulationValue(int newValue) {eval = newValue;};
	int getEvaulationValue() {return eval;};
	void setField(typPole& pole1);
	void setCoor(Coordinates *coors){ coor = coors;};
	Coordinates *getCoor() {return coor;};

	void printField();

	void destroyNode();

};

Node::Node(){
	coor = NULL;
	brother = NULL;
	son = NULL;
	color = eval = 0;
}

void Node::setField(typPole& pole1) {
	for (int i=0;i<rozmerPola;i++){
		for (int j=0;j<rozmerPola;j++){
			pole[i][j] = pole1[i][j];
		}
	}
}
// prida Node na koniec zoznamu
void Node::addBrother(Node *n) {
	Node *tmp = brother;
	if (tmp!=NULL){
		while(tmp->getBrother() != NULL) {
			tmp = tmp->getBrother();
		}
	tmp->brother = n;
	}
	else{
		brother = n;
	}
}

// vrati brata
Node *Node::getBrother() {
	return brother;
}
//vrati posledneho brata
Node *Node::getLastBrother() {
	Node *tmp = brother;
	if (tmp!=NULL){
		while(tmp->getBrother() != NULL) {
			tmp = tmp->getBrother();
		}
	return tmp;
	}
	else{
		return NULL;
	}	
}


void Node::printField(){
	for (int i=0;i<rozmerPola;i++){
		for (int j=0;j<rozmerPola;j++){
			printf("%i",pole[i][j]);
		}
		printf("%s","\n");
	}
	printf("%s","\n");
}


void Node::destroyNode(){
	if (coor !=NULL){
		coor->freeCoordinates();
	}
	//syn
	if (son!=NULL){
		if (son->son!=NULL){
			son->son->destroyNode();
		}
		if (son->brother!=NULL){
			son->brother->destroyNode();
		}
		 delete son;
		 son = NULL;
	}
	//brat
	if (brother!=NULL){
		if (brother->son!=NULL){
			brother->son->destroyNode();
		}
		if (brother->brother!=NULL){
			brother->brother->destroyNode();
		}
		delete brother;
		brother = NULL;
	}

}


// ---------------------
// abstract class
class Hrac {
private:
	int myColor;
protected:
	typPole pole;
	int x,y;//suradnice tahu
	Node *root;
public:
	Hrac();
	virtual void move(typPole &pole, int x, int y) {};
	virtual int evaulate(typPole& pole1,int x,int y, int color) {return 0;};
        virtual bool checkMove(typPole &pole1, int x, int y) {return true;}; //metoda je urcena pre manualneho hraca inak neriesit
        virtual void getHelpMoves(TMemo *memo, typPole& pole1, int color) {}; //metoda je urcena pre manualneho hraca inak neriesit
	int getNumOfMyPieces(typPole& pole1, int color);
	void printField();
	void init();
	void setColor(int newColor) {myColor = newColor;};
	int getColor() {return myColor;};
	Coordinates *getPossibleMoves(typPole &pole1, int currentColor);
	int vratPocetObratenych(typPole& pole1,int x,int y,int currentColor);
	int revertColor(int color){return (color==biely)?cierny:biely;};
	void makeMove(Coordinates *coor, typPole& pole1,int currentColor);
	void buildTree(Coordinates *coor, Node *tmpRoot, int level, int color);
	int evaulateTree(Node *tmpRoot, int color);
	void printTree(Node *tmpRoot);
	int max(int h1, int h2){ return (h1>=h2)? h1: h2; };
	int min(int h1, int h2){ return (h1<h2)? h1: h2; };
	Coordinates *chooseBestOption();
	void destroyTree();
};

Hrac::Hrac() {
	root = new Node();
	root->setField(pole);
	root->setCoor(new Coordinates());
}


int Hrac::getNumOfMyPieces(typPole& pole1, int color) {
	int counter = 0;
	for(int i=0;i<rozmerPola;i++) {
		for(int j=0;j<rozmerPola;j++) {
			if(pole[i][j] == color) {
				counter++;
			}
		}
	}

        return counter;
}

void Hrac::printField(){
	for (int i=0;i<rozmerPola;i++){
		for (int j=0;j<rozmerPola;j++){
			printf("%i",pole[i][j]);
		}
		printf("%s","\n");
	}
	printf("%s","\n");
}


void Hrac::init(){
	printf("%s","inicializacia hraca\n");
	for (int i=0;i<rozmerPola;i++){
		for (int j=0;j<rozmerPola;j++){
			pole[i][j]=prazdne;
			printf("%i",pole[i][j]);
		}
		printf("%s","\n");
	}
}

Coordinates *Hrac::getPossibleMoves(typPole &pole1, int currentColor) {
	Coordinates *first = NULL;
	Coordinates *current;
	//int opacna = revertColor(currentColor);
	bool firstRun = true;
	
	for (int i=0;i<rozmerPola;i++){
		for (int j=0;j<rozmerPola;j++){
			if (vratPocetObratenych(pole1,i,j,currentColor)>0){
				//printf("%i%i%s", i,j,"\n");

				// ak sa da z toho policka potiahnut, pridam ho do pola
				if(firstRun) {
					first =  new Coordinates();
					first->setX(i);
					first->setY(j);
					current = first;
					firstRun = false;
				}
				else {
					Coordinates *tmp = new Coordinates();
					tmp->setX(i);
					tmp->setY(j);
					current->setNext(tmp);
					current = current->getNext();
				}
			}
		}
	}
	return first;
}

int Hrac::vratPocetObratenych(typPole &pole1,int x,int y, int currentColor){
	if (pole1[x][y]==prazdne){
		int opacna = revertColor(currentColor);
		int pocet =0, pomocnyPocet=0;
		//pocitanie potencialnych obratenych
		//smerom vlavo hore
		int i=x-1,j=y-1;
		while (i>=0&&j>=0&& pole1[i][j]==opacna){
			i--;
			j--;
			pomocnyPocet++;
		}
		if (i>=0&&j>=0&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom hore
		pomocnyPocet=0;
		i=x-1,j=y;
		while (i>=0&& pole1[i][j]==opacna){
			i--;
			pomocnyPocet++;
		}
		if (i>=0&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom vpravo hore
		pomocnyPocet=0;
		i=x-1,j=y+1;
		while (i>=0&&j<rozmerPola&& pole1[i][j]==opacna){
			i--;
			j++;
			pomocnyPocet++;
		}
		if (i>=0&&j<rozmerPola&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom vpravo
		pomocnyPocet=0;
		i=x,j=y+1;
		while (j<rozmerPola&& pole1[i][j]==opacna){
			j++;
			pomocnyPocet++;
		}
		if (j<rozmerPola&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom vpravo dole
		pomocnyPocet=0;
		i=x+1,j=y+1;
		while (i<rozmerPola&&j<rozmerPola&& pole1[i][j]==opacna){
			i++;
			j++;
			pomocnyPocet++;
		}
		if (i<rozmerPola&&j<rozmerPola&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom dole
		pomocnyPocet=0;
		i=x+1,j=y;
		while (i<rozmerPola&& pole1[i][j]==opacna){
			i++;
			pomocnyPocet++;
		}
		if (i<rozmerPola&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom vlavo dole
		pomocnyPocet=0;
		i=x+1,j=y-1;
		while (i<rozmerPola&&j>=0&& pole1[i][j]==opacna){
			i++;
			j--;
			pomocnyPocet++;
		}
		if (i<rozmerPola&&j>=0&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		//smerom vlavo
		pomocnyPocet=0;
		i=x,j=y-1;
		while (j>=0&& pole1[i][j]==opacna){
			j--;
			pomocnyPocet++;
		}
		if (j>=0&& pole1[i][j]==currentColor){
			pocet+=pomocnyPocet;
		}

		return pocet;
	}
	else {
		return 0;
	}
}

void Hrac::makeMove(Coordinates *coor, typPole& pole1, int currentColor) {
	if (coor!=NULL){
		int x = coor->getX(), y = coor->getY();
		
		int opacna = revertColor(currentColor);
		// vlozim kamen
		pole1[x][y] = currentColor;
		// otocim ostatne kamene
		int i=x-1,j=y-1;
		while (i>=0&&j>=0&& pole1[i][j]==opacna){
			i--;
			j--;
		}
		if (i>=0&&j>=0&& pole1[i][j]==getColor()){
			i=x-1,j=y-1;
			while (i>=0&&j>=0 && pole1[i][j]==opacna){
					pole1[i][j] = currentColor;
					i--;
					j--;
				}
		}

		//smerom hore
		i=x-1,j=y;
		while (i>=0&& pole1[i][j]==opacna){
			i--;
		}
		if (i>=0&& pole1[i][j]==currentColor){
			i=x-1,j=y;
			while (i>=0&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				i--;
			}
		}

		//smerom vpravo hore
		i=x-1,j=y+1;
		while (i>=0&&j<rozmerPola&& pole1[i][j]==opacna){
			i--;
			j++;
		}
		if (i>=0&&j<rozmerPola&& pole1[i][j]==currentColor){
			i=x-1,j=y+1;
			while (i>=0&&j<rozmerPola&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				i--;
				j++;
			}
		}

		//smerom vpravo
		i=x,j=y+1;
		while (j<rozmerPola&& pole1[i][j]==opacna){
			j++;
		}
		if (j<rozmerPola&& pole1[i][j]==currentColor){
			i=x,j=y+1;
			while (j<rozmerPola&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				j++;
			}
		}

		//smerom vpravo dole
		i=x+1,j=y+1;
		while (i<rozmerPola&&j<rozmerPola&& pole1[i][j]==opacna){
			i++;
			j++;
		}
		if (i<rozmerPola&&j<rozmerPola&& pole1[i][j]==currentColor){
			i=x+1,j=y+1;
			while (i<rozmerPola&&j<rozmerPola&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				i++;
				j++;
			}
		}

		//smerom dole
		i=x+1,j=y;
		while (i<rozmerPola&& pole1[i][j]==opacna){
			i++;
		}
		if (i<rozmerPola&& pole1[i][j]==currentColor){
			i=x+1;
			while (i<rozmerPola&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				i++;
			}
		}

		//smerom vlavo dole
		i=x+1,j=y-1;
		while (i<rozmerPola&&j>=0&& pole1[i][j]==opacna){
			i++;
			j--;
		}
		if (i<rozmerPola&&j>=0&& pole1[i][j]==currentColor){
			i=x+1,j=y-1;
			while (i<rozmerPola&&j>=0&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				i++;
				j--;
			}
		}

		//smerom vlavo
		i=x,j=y-1;
		while (j>=0&& pole1[i][j]==opacna){
			j--;
		}
		if (j>=0&& pole1[i][j]==currentColor){
			i=x,j=y-1;
			while (j>=0&& pole1[i][j]==opacna){
				pole1[i][j] = currentColor;
				j--;
			}
		}
	}

}

void Hrac::buildTree(Coordinates *coor, Node *tmpRoot, int level, int color) {
	Coordinates *tmpCoor = coor;
	if(level<=maxDepth) {
		while(tmpCoor!= NULL) {
			// pomocny node
			Node *tmpSon = new Node();
			// synovi sa prideli otcove hracie pole
			tmpSon->setField(tmpRoot->pole);
			// synovi sa zapamataju koordinaty tahu
			tmpSon->setCoor(new Coordinates(tmpCoor->getX(), tmpCoor->getY()));
			// na hracie pole sa aplikuje tah na prislusne policko
			makeMove(tmpCoor, tmpSon->pole, color);
			
			if(level==maxDepth) {
				// ohodnoti vysledne hracie pole v listoch stromu podla farby hraca, ktory robi tah a zapamata si evaluacnu hodnotu
				int evaluation = evaulate(tmpSon->pole, tmpCoor->getX(), tmpCoor->getY(), color);
				// prevrat hodnotu mina, aby stacilo zistovat max
				tmpSon->setEvaulationValue((color==getColor())?evaluation:-evaluation);
			}
			
			
			if(tmpRoot->getSon() == NULL) {
				// prvy syn
				tmpRoot->setSon(tmpSon);
				// rekurzia na syna
				buildTree(getPossibleMoves(tmpSon->pole,revertColor(color)), tmpRoot->getSon(), level + 1,revertColor(color));
			}
			else {
				// dalsi synovia sa pridavaju ako bratia prveho syna
				tmpRoot->getSon()->addBrother(tmpSon);
				// rekurzia na brata
				Node* lastBrother = tmpRoot->getSon()->getLastBrother();
				buildTree(getPossibleMoves(lastBrother->pole, revertColor(color)), lastBrother, level + 1, revertColor(color));
			}
			
			// posunutie na dalsie koordinaty v zozname
			tmpCoor = tmpCoor->getNext();
		}
		if (tmpCoor!=NULL){
			coor->freeCoordinates();
			delete coor;

		}
	}
}

int Hrac::evaulateTree(Node *tmpRoot, int color) {
	int evaluation =tmpRoot->getEvaulationValue();
	//tmpRoot->printField();
	if(tmpRoot->getSon() != NULL) {
		evaluation = evaulateTree(tmpRoot->getSon(), revertColor(color));
		tmpRoot->setEvaulationValue(evaluation);
	}
	
	//cout << tmpRoot->getEvaulationValue() << ", ";

	if(tmpRoot->getBrother() != NULL) {
		int eval = evaulateTree(tmpRoot->getBrother(), color);
		if (color != getColor()){
			evaluation = max(evaluation,eval);
		}
		else {
			evaluation = min(evaluation, eval);
		}
	}
	else {
		evaluation = tmpRoot->getEvaulationValue();
		//cout << endl;
	}

	return evaluation;
}


void Hrac::printTree(Node *tmpRoot){
	//tmpRoot->printField();
	if(tmpRoot->getSon() != NULL) {
		printTree(tmpRoot->getSon());
	}
	
	//cout << tmpRoot->getEvaulationValue() << ", ";

	
	if(tmpRoot->getBrother() != NULL) {
		printTree(tmpRoot->getBrother());
	}
	//cout << endl;
}

Coordinates *Hrac::chooseBestOption(){
	Node * son = root->getSon();
	if (son!=NULL){
		Node * bestSon = son;
		if (son->getBrother()!=NULL){
			while(son->getBrother()!=NULL){
				if (son->getEvaulationValue() > bestSon->getEvaulationValue()){
					bestSon=son;
				}
				son = son->getBrother();
			}
			return bestSon->getCoor();
		}
		else{
			return son->getCoor();
		}
	}
	else{
		return NULL;
	}
}


void Hrac::destroyTree(){
	if (root==NULL){
		return;
	}
	else {
		root->destroyNode();
	}
}

